#include"Game.h"
#include"Input.h"
#include"Factory.h"

#include<thirdparty\glfw3.h>
#include<thirdparty\glm\glm.hpp>
#include<thirdparty\glm\gtc\matrix_transform.hpp>
#include<thirdparty\glm\gtc\type_ptr.hpp>


namespace Craft
{

	const GLfloat effective_distance = 2.0f;

	void Game::init_data()
	{
		ResourceManger::load_shader("shaders/block.vs", "shaders/block.frag", nullptr, "block");
		ResourceManger::load_shader("shaders/skyBox.vs", "shaders/skyBox.frag", nullptr, "skyBox");
		ResourceManger::load_shader("shaders/sprite.vs", "shaders/sprite.frag", nullptr, "sprite");
		ResourceManger::load_texture("images/line.png", GL_TRUE, "lineBox");
		ResourceManger::load_texture("images/block1.png", GL_TRUE,"block");
		ResourceManger::load_texture("images/crosschair.png", GL_TRUE, "cross");

		TexBlockRenderBuilder::buildDefaultTexRender();

	}

	Game::Game():
		m_Camera(nullptr),
		hitInfo(),
		buildBlock(1)
	{
	}

	Game::~Game()
	{
		delete m_sprite;
		delete m_world;

		for (auto& chunk : m_chunks)
		{
			if (chunk)
				delete chunk;
		}
	}

	void Game::init()
	{
		init_data();

		// 初始化精灵渲染器
		m_sprite = new SpriteRender(ResourceManger::get_shader("sprite"));
		
		// init camera
		m_Camera = std::unique_ptr<Camera>(new Camera(this, glm::vec3(0.0f, 17.0f, 0.0f)));

		// 初始化world
		m_world = new World(*m_Camera);
	}

	void Game::update(double deltaTime)
	{
		// gravity
		m_Camera->accelerateY(-0.05f);
		m_Camera->update();
		m_world->update(deltaTime);

		//render_update(m_Camera->getPostion());
	}

	void Game::draw()
	{
		Shader shader = ResourceManger::get_shader("block").use();
		glEnable(GL_DEPTH_TEST);
	
		glm::mat4 view;
		glm::mat4 projection;
		view = m_Camera->getViewMatrix();
		projection = glm::perspective(45.0f, SCREEN_WIDTH * 1.0f / SCREEN_HEIGHT, 0.1f, 1000.0f);

		shader.SetMatrix4("view", view);
		shader.SetMatrix4("projection", projection);
		shader.SetVector3f("camPos", m_Camera->getPostion());

		// 绘制方块世界
		m_world->render(shader);

		// show lines if catch a block
		if (currBlock.get_Id())
		{
			glActiveTexture(GL_TEXTURE0);
			ResourceManger::get_texutre("lineBox").bind();
			glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
			blockRend.render(shader);
		}

		// sky box
		glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
		sky.render(ResourceManger::get_shader("skyBox"), *m_Camera);


		// crosschair
		shader = ResourceManger::get_shader("sprite").use();
		projection = glm::ortho(0.0f, static_cast<GLfloat>(SCREEN_WIDTH), static_cast<GLfloat>(SCREEN_HEIGHT), 0.0f, -1.0f, 1.0f);
		shader.SetInteger("image", 0);
		shader.SetMatrix4("projection", projection);

		m_sprite->render(ResourceManger::get_texutre("cross"), glm::vec2(SCREEN_WIDTH / 2.0f, SCREEN_HEIGHT / 2.0f), glm::vec2(30, 30), 0, glm::vec3(1.0f));

	}

	void Game::stop()
	{

	}

	void Game::notify_input(double deltaTime)
	{
		GLdouble cameraSpeed = 5.0f * deltaTime;
		bool bGetCurrentBlock = false;

		// select block
		if (InputManger::keys[GLFW_KEY_1])
		{
			buildBlock.set_Id(1);
			buildBlock.set_lightValue(1);
			buildBlock.set_type(tBLOCK);
		}
		if (InputManger::keys[GLFW_KEY_2])
		{
			buildBlock.set_Id(2);
			buildBlock.set_lightValue(1);
			buildBlock.set_type(tBLOCK);
		}
		if (InputManger::keys[GLFW_KEY_3])
		{
			buildBlock.set_Id(3);
			buildBlock.set_lightValue(10);
			buildBlock.set_type(tBLOCK);
		}
		if (InputManger::keys[GLFW_KEY_4])
		{
			buildBlock.set_Id(4);
			buildBlock.set_lightValue(1);
			buildBlock.set_type(tBLOCK);
		}
		if (InputManger::keys[GLFW_KEY_5])
		{
			buildBlock.set_Id(5);
			buildBlock.set_lightValue(1);
			buildBlock.set_type(tBLOCK);
		}
		if (InputManger::keys[GLFW_KEY_6])
		{
			buildBlock.set_Id(6);
			buildBlock.set_lightValue(1);
			buildBlock.set_type(tPLANT);
		}
		if (InputManger::keys[GLFW_KEY_F1])
		{
			save_chunks();
			InputManger::keys[GLFW_KEY_F1] = 0;
		}

		if (InputManger::keys[GLFW_KEY_F2])
		{
			load_chunks();
			//chunk1->set_update(true);
			InputManger::keys[GLFW_KEY_F2] = 0;
		}


		// camera
		if (InputManger::keys[GLFW_KEY_W])
		{
			bGetCurrentBlock = true;
			m_Camera->processKeyBoard(FORWARD, deltaTime);
		}

		if (InputManger::keys[GLFW_KEY_S])
		{
			bGetCurrentBlock = true;
			m_Camera->processKeyBoard(BACKWARD, deltaTime);
		}

		if (InputManger::keys[GLFW_KEY_A])
		{
			bGetCurrentBlock = true;
			m_Camera->processKeyBoard(LEFT, deltaTime);
		}
		if (InputManger::keys[GLFW_KEY_D])
		{
			bGetCurrentBlock = true;
			m_Camera->processKeyBoard(RIGHT, deltaTime);
		}
		if (InputManger::keys[GLFW_KEY_SPACE])
		{
			bGetCurrentBlock = true;
			m_Camera->accelerateY(0.9f);
			InputManger::keys[GLFW_KEY_SPACE] = 0;
		}


		// block
		if (InputManger::keys[GLFW_MOUSE_BUTTON_LEFT])
		{
			bGetCurrentBlock = true;
			try_build_block();
			InputManger::keys[GLFW_MOUSE_BUTTON_LEFT] = 0;
		}

		if (InputManger::keys[GLFW_MOUSE_BUTTON_RIGHT])
		{
			try_destory_block();
			InputManger::keys[GLFW_MOUSE_BUTTON_RIGHT] = 0;
		}

		if (InputManger::offsetX != 0 || InputManger::offsetY != 0)
		{
			bGetCurrentBlock = true;
			m_Camera->processMouseMovement(InputManger::offsetX, InputManger::offsetY,deltaTime);
			InputManger::offsetX = 0;
			InputManger::offsetY = 0;
		}

		if (bGetCurrentBlock)
			get_current_block();
		
	}

	void Game::get_current_block()
	{
		currBlock = m_world->get_current_block();
		
		if (currBlock.get_Id())
			blockRend.generate_Block(currBlock);

	}

	/*
		@brief 建立block

			   如果准星已经捕获一个方块,currBlock不为空气块(id > 0)
			   则在currBlock的指向的面创建一个block，这个block属性由
			   build_block的状态指定
	*/
	void Game::try_build_block()
	{
		m_world->try_build_block(buildBlock);
	}


	/*
		@brief 删除当前选的block
			   获取当前要删除的block对应存储的chunk_id
			   由对应chunk删除指定block后设置为更新态
	*/
	void Game::try_destory_block()
	{
		currBlock = m_world->try_destory_block();
	}


	void Game::save_chunks()
	{
		for (auto& chunk : m_chunks)
		{
			chunk->save_chunks();
		}
	}

	void Game::load_chunks()
	{
		for (auto& chunk : m_chunks)
		{
			chunk->load_chunks();
		}
	}


	World* Game::get_world()
	{
		return m_world;
	}
}

